package session

import (
	"encoding/json"
	"errors"
	"net/http"
	"time"

	"gitee.com/shoyer/framework/keys"
	"gitee.com/shoyer/framework/runtime"
	"gitee.com/shoyer/framework/start"
	"gitee.com/shoyer/logger"
	"gitee.com/shoyer/service/config"
	"gitee.com/shoyer/service/mail"
	"gitee.com/shoyer/service/rdb"
	"gitee.com/shoyer/util"
	"github.com/go-redis/redis"
)

// Session session
type Session struct {
	ID        string
	UID       uint
	IP        string
	UUID      string
	Platfrom  string
	Version   string
	Channel   string
	Locale    string
	IsLogin   bool
	UpdatedAt int64
	OnServer  string
	BizData   interface{} // 用于存放业务相关的数据, 可以在业务中定义BizSession，用于自定义的属性数据存取。注意不要用map, map结构不利于重构
}

func NewSession() *Session {
	s := &Session{}
	s.BizData = start.GetBizSession()
	return s
}

func GetSessionID(sid string) *Session {
	result, _ := rdb.GetDB().Get(rdb.GetKey(keys.Session, sid)).Bytes()
	if util.IsEmptyArrayBytes(result) {
		return nil
	}

	session := NewSession()
	err := json.Unmarshal(result, &session)
	if err != nil {
		logger.Errorf(err.Error())
		return nil
	}

	if session.ID != "" {
		return session
	}

	return nil
}

func CreateHttpSession(w http.ResponseWriter) *Session {
	session := NewSession()
	session.OnServer = runtime.GetServerIP()

	session.ID = util.GetRandomString(5)
	for session.IsExist() {
		session.ID = util.GetRandomString(5)
	}

	session.IsLogin = false
	session.Save()
	session.SaveToMaster() // 用来作全局唯一检测

	http.SetCookie(w, &http.Cookie{
		Name:  "s",
		Value: session.ID,
		Path:  "/",
	})

	return session
}

// SaveToMaster SaveToMaster
func (session *Session) SaveToMaster() (bool, error) {
	return session.SaveSession(rdb.GetMaster())
}

// SaveRemote SaveRemote
func (session *Session) SaveRemote(server string) (bool, error) {
	return session.SaveSession(rdb.GetRemote(server))
}

// Save Save
func (session *Session) Save() (bool, error) {
	return session.SaveSession(rdb.GetDB())
}

func (session *Session) SaveSession(redis *redis.Client) (bool, error) {
	if redis == nil {
		logger.Error("can not get redis client")
		return false, errors.New("can not get redis client")
	}

	if session.ID == "" {
		logger.Error("session.ID is nil")
		return false, errors.New("session.ID is nil")
	}

	session.UpdatedAt = util.GetMillisecond()

	jsonBytes, err := json.Marshal(session)
	if err != nil {
		logger.Error(err.Error())
		return false, err
	}

	_, err1 := redis.Set(rdb.GetKey(keys.Session, session.ID), jsonBytes, time.Hour*12).Result()
	if err1 != nil {
		logger.Error(err1)

		title := "严重服务器错误"
		content := "Redis数据写入异常，请立即排查， session.saveSession: " + util.ByteToString(jsonBytes)
		ok, err2 := mail.SendSSL(config.GetAdminEmail(), title, content)
		if !ok {
			logger.Error(err2, content)
			return false, err2
		}

		return false, err1
	}

	return true, nil
}

// IndexByUID IndexByUID
func (session *Session) IndexByUID() (bool, error) {
	return session.indexByUID(rdb.GetDB())
}

func (session *Session) indexByUID(redis *redis.Client) (bool, error) {
	if redis == nil {
		logger.Error("can not get redis client")
		return false, errors.New("can not get redis client")
	}

	if session.ID == "" {
		logger.Error("session.ID is nil")
		return false, errors.New("session.ID is nil")
	}

	if session.UID == 0 {
		logger.Error("session.UID is nil")
		return false, errors.New("session.UID is nil")
	}

	return redis.HSet(rdb.GetKey(keys.SessionByUID), util.UintToString(session.UID), session.ID).Result()
}

// GetByUID GetByUID
func (session *Session) GetByUID(uid uint) *Session {
	redis := rdb.GetDB()
	sessionID, _ := redis.HGet(rdb.GetKey(keys.SessionByUID), util.UintToString(uid)).Result()
	result, _ := redis.Get(rdb.GetKey(keys.Session, sessionID)).Bytes()
	if util.IsEmptyArrayBytes(result) {
		return nil
	}

	_ = json.Unmarshal(result, &session)

	return session
}

// GetSessionBySessionID GetSessionBySessionID
func GetSessionBySessionID(sessionID string) *Session {
	result, _ := rdb.GetDB().Get(rdb.GetKey(keys.Session, sessionID)).Bytes()
	if util.IsEmptyArrayBytes(result) {
		return nil
	}

	session := NewSession()
	err1 := json.Unmarshal(result, &session)
	if err1 != nil {
		return nil
	}

	if session.ID != "" {
		return session
	}

	return nil
}

// IsExist IsExist
func (session *Session) IsExist() bool {
	result, _ := rdb.GetMaster().Get(rdb.GetKey(keys.Session, session.ID)).Result()
	return result != "" || len(result) > 0
}

// IsMySession IsMySession
func (session *Session) IsMySession() bool {
	return session.OnServer == runtime.GetServerIP()
}

// Delete Delete
// func (session *Session) Delete() {
// 	redis.GetRedis().HDel(rediskey.Session, session.ID)
// 	redis.GetRedis().HDel(rdb.GetKey(keys.SessionByUID), util.UintToString(session.UID))
// 	redis.GetRedis().Del(rdb.GetKey(keys.Session, session.ID))
// 	rdb.GetMaster().Del(rdb.GetKey(keys.Session, session.ID))
// }

// InactiveOtherSessions InactiveOtherSessions
// func (session Session) InactiveOtherSessions() {
// 	redis := redis.GetRedis()
// 	uid := fmt.Sprintf("%d", session.UID)

// 	// 把原来的session设置为非激活状态。
// 	result, _ := redis.HGet(rediskey.UserSessions, uid).Result()
// 	sessionKeys := util.Split(result, ",")
// 	if len(sessionKeys) > 0 {
// 		for index, sessionKey := range sessionKeys {
// 			result, _ := redis.HGet(rediskey.Session, sessionKey).Result()
// 			if result != "" {
// 				otherSession := Session{}
// 				_ = json.Unmarshal([]byte(result), &otherSession)
// 				if otherSession.UUID == session.UUID { // 同一设备，重新登录，创建了新的session.
// 					otherSession.Delete()
// 					sessionKeys = append(sessionKeys[:index], sessionKeys[index+1:]...)
// 				} else { // 不同设备，暂时保留
// 					otherSession.IsActive = false
// 					otherSession.Save()
// 				}
// 			}
// 		}
// 	}

// 	sessionKeys = append(sessionKeys, session.ID)
// 	redis.HSet(rediskey.UserSessions, uid, strings.Join(sessionKeys, ","))
// }
